﻿/*------------------------------------------------------------------------

CUBEMAPTEXTURERD.HPP v1.1.2

Copyright 楊志賢 CxxlMan, 2014 - 2015
All Rights Reserved

------------------------------------------------------------------------*/

#if !defined(__CUBEMAPTEXTURERD_HPP_Cxxl3D)
#define __CUBEMAPTEXTURERD_HPP_Cxxl3D

#include <Sys\VERTEXFMT.HPP>
#include <Sys\RENDERUNIT.HPP>
#include <Sys\RenderUnit\SYSDDCLASSID.HPP>
#include <Sys\CSMART_PTR.HPP>
#include <Sys\CSMART_ARRAY.HPP>
#include <Sys\RenderUnit\SysRenderDatagram\TEXTURERD.HPP>

namespace Cxxl3D
{

// 傳遞給 ICubemapTextureRD 用的資料結構
class dataCubemapTexture_2D
{
  CxxlMan::CriticalSec csObject;

  C3D_UINT m_Width, m_Height;
  CxxlMan::CSmart_Array<const tagPIXEL> m_Texture_Array_Front; // -Z
  CxxlMan::CSmart_Array<const tagPIXEL> m_Texture_Array_Back;  // +Z
  CxxlMan::CSmart_Array<const tagPIXEL> m_Texture_Array_Left;  // -X
  CxxlMan::CSmart_Array<const tagPIXEL> m_Texture_Array_Right; // +X
  CxxlMan::CSmart_Array<const tagPIXEL> m_Texture_Array_Up;    // +Y
  CxxlMan::CSmart_Array<const tagPIXEL> m_Texture_Array_Down;  // -Y
public:

  void cxxlFASTCALL LockTexture() const
  {
    csObject.Enter();
  }

  void cxxlFASTCALL UnLockTexture() const
  {
    csObject.Leave();
  }

  // Destructor
  ~dataCubemapTexture_2D()
  {
  }

  // Constructor
  dataCubemapTexture_2D(
      C3D_UINT Width, C3D_UINT  Height,
      const CxxlMan::CSmart_Array<const tagPIXEL> &Front,
      const CxxlMan::CSmart_Array<const tagPIXEL> &Back,
      const CxxlMan::CSmart_Array<const tagPIXEL> &Left,
      const CxxlMan::CSmart_Array<const tagPIXEL> &Right,
      const CxxlMan::CSmart_Array<const tagPIXEL> &Up,
      const CxxlMan::CSmart_Array<const tagPIXEL> &Down
    )
   :m_Width(Width), m_Height(Height),
    m_Texture_Array_Front(Front),
    m_Texture_Array_Back(Back),
    m_Texture_Array_Left(Left),
    m_Texture_Array_Right(Right),
    m_Texture_Array_Up(Up),
    m_Texture_Array_Down(Down)
  {    
  }

  // Copy Constructor
  dataCubemapTexture_2D(const dataCubemapTexture_2D &Src)
   :m_Texture_Array_Front(NULL),
    m_Texture_Array_Back(NULL),
    m_Texture_Array_Left(NULL),
    m_Texture_Array_Right(NULL),
    m_Texture_Array_Up(NULL),
    m_Texture_Array_Down(NULL)
  {
    Src.LockTexture();

    m_Width = Src.m_Width;
    m_Height = Src.m_Height;

    m_Texture_Array_Front = Src.m_Texture_Array_Front;
    m_Texture_Array_Back = Src.m_Texture_Array_Back;
    m_Texture_Array_Left = Src.m_Texture_Array_Left;
    m_Texture_Array_Right = Src.m_Texture_Array_Right;
    m_Texture_Array_Up = Src.m_Texture_Array_Up;
    m_Texture_Array_Down = Src.m_Texture_Array_Down;

    Src.UnLockTexture();
  }

  // Assign
  dataCubemapTexture_2D & operator=(const dataCubemapTexture_2D &Src)
  {
    Src.LockTexture();

    C3D_UINT tmpWidth = Src.m_Width;
    C3D_UINT tmpHeight = Src.m_Height;

    CxxlMan::CSmart_Array<const tagPIXEL> tmpTexture_Array_Front = Src.m_Texture_Array_Front;
    CxxlMan::CSmart_Array<const tagPIXEL> tmpTexture_Array_Back = Src.m_Texture_Array_Back;
    CxxlMan::CSmart_Array<const tagPIXEL> tmpTexture_Array_Left = Src.m_Texture_Array_Left;
    CxxlMan::CSmart_Array<const tagPIXEL> tmpTexture_Array_Right = Src.m_Texture_Array_Right;
    CxxlMan::CSmart_Array<const tagPIXEL> tmpTexture_Array_Up = Src.m_Texture_Array_Up;
    CxxlMan::CSmart_Array<const tagPIXEL> tmpTexture_Array_Down = Src.m_Texture_Array_Down;

    Src.UnLockTexture();

    this->LockTexture();

    m_Width =  tmpWidth;
    m_Height = tmpHeight;

    m_Texture_Array_Front = tmpTexture_Array_Front;
    m_Texture_Array_Back = tmpTexture_Array_Back;
    m_Texture_Array_Left = tmpTexture_Array_Left;
    m_Texture_Array_Right = tmpTexture_Array_Right;
    m_Texture_Array_Up = tmpTexture_Array_Up;
    m_Texture_Array_Down = tmpTexture_Array_Down;

    this->UnLockTexture();

    return *this;
  }

  void cxxlFASTCALL GetData(C3D_UINT &Width, C3D_UINT &Height, 
      CxxlMan::CSmart_Array<const tagPIXEL> &Front, // -Z
      CxxlMan::CSmart_Array<const tagPIXEL> &Back,  // +Z
      CxxlMan::CSmart_Array<const tagPIXEL> &Left,  // -X
      CxxlMan::CSmart_Array<const tagPIXEL> &Right, // +X
      CxxlMan::CSmart_Array<const tagPIXEL> &Up,    // +Y
      CxxlMan::CSmart_Array<const tagPIXEL> &Down   // -Y
    ) const
  {
    this->LockTexture();

    Width = m_Width;
    Height = m_Height;

    Front = m_Texture_Array_Front;
    Back = m_Texture_Array_Back;
    Left = m_Texture_Array_Left;
    Right = m_Texture_Array_Right;
    Up = m_Texture_Array_Up;
    Down = m_Texture_Array_Down;

    this->UnLockTexture();
  }
};

class ICubemapTextureRD;

// 供 Render 單元要求 ICubemapTextureRD 重新取得 Texture 資料時，藉由此介面取得
class IRetrieveCubemapTexture:virtual public CxxlMan::cxxlPreserve2
{

protected:
  // 儲存容器藉此取得要永續存取的屬性資料
  // 延伸類別須以 virtual 的形式覆寫
  // 並且須呼叫上層類別的此函數
  virtual bool cxxlFASTCALL Ref(CxxlMan::Filter *F)
  {
    if(F != NULL && CxxlMan::cxxlPreserve2::Ref(F))
      return true;
    else
      return false;
  }

  // Constructor
  IRetrieveCubemapTexture()
    :cxxlObject(CxxlMan::Spirit_Easy),
    CxxlMan::cxxlPreserve2(L"") // 在建構時指定 ClassID
  {
  }

  // Destructor
  ~IRetrieveCubemapTexture()
  {
  }

public:

  // 須實作
  virtual void cxxlFASTCALL GetTexture(const CxxlMan::Smart_Ptr<ICubemapTextureRD> &);
};

class ICubemapTextureBridge;

// 提供天空盒的六面圖片
class ICubemapTextureRD:public ITextureRD, public DepositStore
{
  virtual void cxxlFASTCALL SysInit(const CxxlMan::UNICODE_String &CubemapTextureID) = 0;

  // 這裡須傳回 IRenderDatagram 的 "實作介面"，即被 cxxlCM_GetElement() 叫用的 InterfaceID
  CxxlMan::UNICODE_String cxxlFASTCALL GetRenderDatagramID() const
  {
    return ICubemapTextureRD_ClassID;
  }

  // 傳回 IRenderDatagram 的 "實作介面"
  virtual void *cxxlFASTCALL GetDefaultInterface()
  {
    return this;
  }

  // 供 Render 單元取得 6 面的 Mipmap
  virtual bool cxxlFASTCALL GetMipmap(const unsigned char ****Mipmaps, const unsigned int ***WidthHeight, unsigned int *LevelNum) const = 0;

  // 供 Render 單元取得屬性資料 
  virtual void cxxlFASTCALL GetAttr(unsigned char &TextureAddrMode, unsigned int &TextureClampBorderRGBA, unsigned char &MipmapMode, char &AutoTexCoordIndexMode, unsigned char &BlendMode, CxxlMan::CSmart_Ptr<const C3DMatrix4x4> &TextureMatrix_Arg) const = 0;

protected:


  // 儲存容器藉此取得要永續存取的屬性資料
  // 延伸類別須以 virtual 的形式覆寫
  // 並且須呼叫上層類別的此函數
  virtual bool cxxlFASTCALL Ref(CxxlMan::Filter *F)
  {
    if(F != NULL && CxxlMan::cxxlPreserve2::Ref(F))
      return true;
    else
      return false;
  }

  // Constructor
  ICubemapTextureRD()
    :cxxlObject(CxxlMan::Spirit_Easy),
    CxxlMan::cxxlPreserve2(ICubemapTextureRD_ClassID) // 在建構時指定 ClassID
  {
  }

  // Destructor
  ~ICubemapTextureRD()
  {
  }

public:

  // 取得天空盒貼圖資料識別，IRetrieveTexture 的實作須有能力藉此將正確的頂點資料由 MipmapMake() 置入
  virtual CxxlMan::UNICODE_String cxxlFASTCALL GetCubemapTextureID() = 0;

  // 使用 dataCubemapTexture_2D 建立 6 面的 Mipmap
  virtual void cxxlFASTCALL MipmapMake(const CxxlMan::CSmart_Ptr<const dataCubemapTexture_2D> &CubemapTexture_Arg) = 0;

  // 通知其儲存的 Mipmap 所依據的 dataCubemapTexture_2D 資料已被修改，只有 Crushable() 設定過才能再去取得，否則仍使用儲存的 Mipmap。
  // 若未使用 Crushable()，不如再用 MipmapMake() 更新資料
  virtual void cxxlFASTCALL Modified() = 0;

  // 指示當 Render 單元取出 Mipmap 資料後，可以將之清除，內定是不清除的。
  // 須提供一個 IRetrieveCubemapTexture，作為須取出 Mipmap 資料時的處理方法
  // IRetrieveCubemapTexture_Arg 不可為 NULL，否則 Crushable() 呼叫無效
  virtual void cxxlFASTCALL Crushable(const CxxlMan::Smart_Ptr<IRetrieveCubemapTexture> &IRetrieveCubemapTexture_Arg) = 0;

  // 建立 ICubemapTextureRD 物件，失敗傳回 NULL
  // CubemapTextureID = 天空盒貼圖資料識別
  static CxxlMan::Smart_Ptr<ICubemapTextureRD> cxxlFASTCALL Create(const CxxlMan::UNICODE_String &CubemapTextureID)
  {
    CxxlMan::Smart_Ptr<ICubemapTextureRD> r = CxxlMan::Preserve2_Cast<ICubemapTextureRD>( CxxlMan::cxxlCM_GetElement(ICubemapTextureRD_ClassID, IRenderDatagram_Group) );

    if(r.isNULL() == false)
      r->SysInit(CubemapTextureID);

    return r;
  }

  friend class ICubemapTextureBridge;
  friend class CxxlMan::IcxxlStorage;          // Preserve2 元件須加上這個
  friend class CxxlMan::Smart_Ptr<ICubemapTextureRD>;  // Preserve2 元件須加上這個


};

// Render 單元要叫用 ICubemapTextureRD 提供給其叫用的功能，須繼承此介面
class ICubemapTextureBridge
{
protected:
  // 由 Render 單元叫用，以取得 Mipmap 資料，若無法取得回覆 false
  bool cxxlFASTCALL GetMipmap(const unsigned char ****Mipmaps, const unsigned int ***WidthHeight, unsigned int *LevelNum, const CxxlMan::Smart_Ptr<ICubemapTextureRD> &ICubemapTextureRD_Arg) const
  {
    return ICubemapTextureRD_Arg->GetMipmap(Mipmaps, WidthHeight, LevelNum);
  }

  // 由 Render 單元叫用，以取得屬性資料
  void cxxlFASTCALL GetAttr( unsigned char &TextureAddrMode, unsigned int &TextureClampBorderRGBA, unsigned char &MipmapMode, char &AutoTexCoordIndexMode, unsigned char &BlendMode, CxxlMan::CSmart_Ptr<const C3DMatrix4x4> &TextureMatrix_Arg, 
    const CxxlMan::Smart_Ptr<ICubemapTextureRD> &ICubemapTextureRD_Arg) const
  {
    ICubemapTextureRD_Arg->GetAttr(TextureAddrMode, TextureClampBorderRGBA, MipmapMode, AutoTexCoordIndexMode, BlendMode, TextureMatrix_Arg);
  }

public:
  ICubemapTextureBridge(){}
  ~ICubemapTextureBridge(){}

};



}   /* namespace Cxxl3D */
#endif
